/*!
 * \file ems_xstream.c
 *
 * \brief 主要对应TibjmsxStream类
 *
 * All Rights Reserved Copyright (C) 2014 broada,Co.,Ltd.
 *
 * \version 1.0
 * \author songbb
 * \date 2016/10/24
 *
 */

#include <iconv2utf8.h>
#include "conntrack.h"
#include "frct_comm.h"
#include "frzlib.h"

#include "ems_core.h"
#include "ems_comm.h"
#include "ems_xstream.h"

union int_float_bits {
    int32_t int_bits;
    float float_bits;
};

union int_double_bits {
    int64_t int_bits;
    double double_bits;
};

int ems_readUnsignedByte(L7_ems_packet *p_st_ems_packet
                        , unsigned char *p_uc_out)
{
    uint8_t byte;

    if (bytebuff_get_char(p_st_ems_packet->p_st_packet, &byte) != FR_SUCC)
        goto error;

    *p_uc_out = byte;

    return FR_SUCC;
error:
    return FR_FAIL;
}

int ems_readByte(L7_ems_packet *p_st_ems_packet
                , char *p_c_out)
{
    uint8_t byte;

    if (bytebuff_get_char(p_st_ems_packet->p_st_packet, &byte) != FR_SUCC)
        goto error;

    *p_c_out = (char) byte;

    return FR_SUCC;
error:
    return FR_FAIL;
}

int ems_readBoolean(L7_ems_packet *p_st_ems_packet
                    , char *p_c_out)
{
    uint8_t byte;

    if (bytebuff_get_char(p_st_ems_packet->p_st_packet, &byte) != FR_SUCC)
        goto error;

    *p_c_out = (char) (byte != 0 ? FR_YES : FR_NO);

    return FR_SUCC;
error:
    return FR_FAIL;
}

int ems_readUnsignedShort(L7_ems_packet *p_st_ems_packet
                        , unsigned short *p_us_out)
{
    uint16_t u16;

    if (bytebuff_get_short(p_st_ems_packet->p_st_packet, 2, &u16, BYTE_BIG_ENDIAN) != FR_SUCC)
        goto error;

    *p_us_out = u16;

    return FR_SUCC;
error:
    return FR_FAIL;
}

int ems_readChar(L7_ems_packet *p_st_ems_packet
                , short *p_s_out)
{
    uint16_t u16;

    if (bytebuff_get_short(p_st_ems_packet->p_st_packet, 2, &u16, BYTE_BIG_ENDIAN) != FR_SUCC)
        goto error;

    *p_s_out = (short) u16;

    return FR_SUCC;
error:
    return FR_FAIL;
}

int ems_readShort(L7_ems_packet *p_st_ems_packet
                , short *p_s_out)
{
    return ems_readChar(p_st_ems_packet, p_s_out);
}

int ems_readInt(L7_ems_packet *p_st_ems_packet
                , int *p_i_out)
{
    uint32_t u32;

    if (bytebuff_get_int(p_st_ems_packet->p_st_packet, 4, &u32, BYTE_BIG_ENDIAN) != FR_SUCC)
        goto error;

    *p_i_out = (int) u32;

    return FR_SUCC;
error:
    return FR_FAIL;
}

int ems_readLong(L7_ems_packet *p_st_ems_packet
                , long long *p_ll_out)
{
    uint64_t u64;

    if (bytebuff_get_long(p_st_ems_packet->p_st_packet, 8, &u64, BYTE_BIG_ENDIAN) != FR_SUCC)
        goto error;

    *p_ll_out = (long long) u64;

    return FR_SUCC;
error:
    return FR_FAIL;
}

int ems_readFloat(L7_ems_packet *p_st_ems_packet
                , float *p_f_out)
{
    int i_tmp = 0;

    if (bytebuff_get_int(p_st_ems_packet->p_st_packet, 4, (unsigned int *) &i_tmp, BYTE_BIG_ENDIAN) != FR_SUCC)
        return FR_FAIL;
    union int_float_bits bits;
    bits.int_bits = i_tmp;
    *p_f_out = bits.float_bits;

    return FR_SUCC;
}

int ems_readDouble(L7_ems_packet *p_st_ems_packet
                , double *p_d_out)
{
    long long ll_tmp = 0LL;

    if (bytebuff_get_long(p_st_ems_packet->p_st_packet, 8, (uint64_t *) &ll_tmp, BYTE_BIG_ENDIAN) != FR_SUCC)
        return FR_FAIL;
    union int_double_bits double_bits;
    double_bits.int_bits = ll_tmp;
    *p_d_out = double_bits.double_bits;

    return FR_SUCC;
}

int ems_readBytes(L7_ems_packet *p_st_ems_packet
                , char **pp_out
                , int *p_i_out_len)
{
    long long ll_tmp;
    int j;
    char *p_out = NULL;

    if (ems_readVarLong(p_st_ems_packet, &ll_tmp) != FR_SUCC)
        goto error;

    if (ll_tmp == -1LL || ll_tmp == 0)
    {
        *pp_out = NULL;
        *p_i_out_len = 0;
        return FR_SUCC;
    }

    j = (int) ll_tmp;

    if ((p_out = (char *) frmalloc(sizeof(char) * j)) == NULL)
    {
        EMS_MALLOCK_ERR;
        goto error;
    }
    if (bytebuff_get_bytes(p_st_ems_packet->p_st_packet, p_out, (unsigned int) j) != FR_SUCC)
        goto error;

    *pp_out = p_out;
    *p_i_out_len = j;

    return FR_SUCC;
error:
    return FR_FAIL;
}

int ems_skipShort(L7_ems_packet *p_st_ems_packet)
{
    return bytebuff_skip(p_st_ems_packet->p_st_packet, 2);
}

int ems_skipInt(L7_ems_packet *p_st_ems_packet)
{
    return bytebuff_skip(p_st_ems_packet->p_st_packet, 4);
}

int ems_skipLong(L7_ems_packet *p_st_ems_packet)
{
    return bytebuff_skip(p_st_ems_packet->p_st_packet, 8);
}

int ems_skipFloat(L7_ems_packet *p_st_ems_packet)
{
    return bytebuff_skip(p_st_ems_packet->p_st_packet, 4);
}

int ems_skipDouble(L7_ems_packet *p_st_ems_packet)
{
    return bytebuff_skip(p_st_ems_packet->p_st_packet, 8);
}

int ems_readVarLong(L7_ems_packet *p_st_ems_packet
                    , long long *p_ll_out)
{
    uint8_t byte1;
    *p_ll_out = 0LL;
    int j, k;

    if (bytebuff_get_char(p_st_ems_packet->p_st_packet, &byte1) != FR_SUCC)
        goto error;

    if (byte1 == 0xF0)
    {
        *p_ll_out = -1LL;
        return FR_SUCC;
    }

    if ((byte1 & 0x80) == 0)
    {
        *p_ll_out = byte1;
        return FR_SUCC;
    }

    if ((byte1 & 0x40) == 0)
    {
        uint8_t byte2;
        if (bytebuff_get_char(p_st_ems_packet->p_st_packet, &byte2) != FR_SUCC)
            goto error;

        *p_ll_out = ((byte1 << 8) + (byte2 & 0xFF)) & 0x3FFF;
        return FR_SUCC;
    }

    if ((byte1 & 0x20) == 0)
    {
        uint8_t byte2;
        uint8_t byte3;
        uint8_t byte4;
        if (bytebuff_get_char(p_st_ems_packet->p_st_packet, &byte2) != FR_SUCC)
            goto error;
        if (bytebuff_get_char(p_st_ems_packet->p_st_packet, &byte3) != FR_SUCC)
            goto error;
        if (bytebuff_get_char(p_st_ems_packet->p_st_packet, &byte4) != FR_SUCC)
            goto error;

        *p_ll_out = ((byte1 << 24) + (byte2 << 16 & 0xFF0000) + (byte2 << 8 & 0xFF00) + (byte2 & 0xFF)) & 0x1FFFFFFF;
        return FR_SUCC;
    }

    j = byte1 & 0xF;
    for (k = 0; k < j; k++)
    {
        uint8_t byte;
        if (bytebuff_get_char(p_st_ems_packet->p_st_packet, &byte) != FR_SUCC)
            goto error;
        *p_ll_out <<= 8;
        *p_ll_out |= byte & 0xFF;
    }

    return FR_SUCC;
error:
    return FR_FAIL;
}

int ems_skipVarLong(L7_ems_packet *p_st_ems_packet)
{
    uint8_t byte1;
    int j;

    if (bytebuff_get_char(p_st_ems_packet->p_st_packet, &byte1) != FR_SUCC)
        goto error;

    if (byte1 == 0xF0)
        return FR_SUCC;

    if ((byte1 & 0x80) == 0)
        return FR_SUCC;

    if ((byte1 & 0x40) == 0)
    {
        if (bytebuff_skip(p_st_ems_packet->p_st_packet, 1) != FR_SUCC)
            goto error;
        return FR_SUCC;
    }

    if ((byte1 & 0x20) == 0)
    {
        if (bytebuff_skip(p_st_ems_packet->p_st_packet, 3) != FR_SUCC)
            goto error;
        return FR_SUCC;
    }

    j = byte1 & 0xF;
    if (bytebuff_skip(p_st_ems_packet->p_st_packet, (size_t) j) != FR_SUCC)
        goto error;

    return FR_SUCC;
error:
    return FR_FAIL;
}

int ems_getTibjmsEncoding(L7session_ems *p_st_sess)
{
    if (p_st_sess->p_encoding != NULL)
    {
        if (strcmp(p_st_sess->p_encoding, "US-ASCII") == 0 || strcmp(p_st_sess->p_encoding, "ASCII") == 0)
            return 0;
        if (strcmp(p_st_sess->p_encoding, "Cp1252") == 0)
            return 2;
        if (strcmp(p_st_sess->p_encoding, "8859-1") == 0 || strcmp(p_st_sess->p_encoding, "8859_1") == 0)
            return 1;
        if (strcmp(p_st_sess->p_encoding, "8859-15") == 0 || strcmp(p_st_sess->p_encoding, "8859_15") == 0)
            return 9;
        if (strcmp(p_st_sess->p_encoding, "UTF8") == 0 || strcmp(p_st_sess->p_encoding, "UTF-8") == 0)
            return 8;
        return -1;
    }

    return 8;
}

int ems_readString(L7session_ems *p_st_sess
                , L7_ems_packet *p_st_ems_packet
                , int i_boolean
                , char **pp_out
                , int *p_i_out_len)
{
    int i = ems_getTibjmsEncoding(p_st_sess);
    int n = 0;
    char *p_string = NULL;
    char *p_out = NULL;
    unsigned int ui_out_len = 0;

    if (i_boolean == FR_YES)
    {
        if (ems_readUnsignedByte(p_st_ems_packet, (unsigned char *) &n) != FR_SUCC)
            goto error;
        n--;
    }
    else
    {
        long long ll_tmp = 0;
        if (ems_readVarLong(p_st_ems_packet, &ll_tmp) != FR_SUCC)
            goto error;
        if (ll_tmp == -1LL)
        {
            *pp_out = NULL;
            *p_i_out_len = 0;
            return FR_SUCC;
        }

        n = (int) ll_tmp;
        if (n <= 0)
            goto error;
    }

    if (n == 1)
    {
        if (bytebuff_skip(p_st_ems_packet->p_st_packet, 1) != FR_SUCC)
            goto error;
        *pp_out = frstrdup("");
        *p_i_out_len = 1;
        return FR_SUCC;
    }

    if (n > bytebuff_remaining(p_st_ems_packet->p_st_packet))
        goto error;

    // XXX: 以下部分未按源码实现，如有问题请重新编码
    if ((p_string = (char *) frmalloc(sizeof(char) * n)) == NULL)
    {
        EMS_MALLOCK_ERR;
        goto error;
    }
    if (bytebuff_get_bytes(p_st_ems_packet->p_st_packet, p_string, (unsigned int) n) != FR_SUCC)
        goto error;
    if (i == 8)
    {
        *pp_out = p_string;
        *p_i_out_len = n;
    }
    else
    {
        if (iconv_to_utf8(p_string, (unsigned int) n, p_st_sess->p_encoding, &p_out, &ui_out_len) != FR_SUCC)
            goto error;
        *pp_out = p_out;
        *p_i_out_len = ui_out_len;
        ems_free((void **) &p_string);
    }

    return FR_SUCC;
error:
    ems_free((void **) &p_string);
    return FR_FAIL;
}

int ems_readRvSubject(L7session_ems *p_st_sess
                    , L7_ems_packet *p_st_ems_packet
                    , char **pp_out
                    , int *p_i_out_len)
{
    unsigned short us_i;
    unsigned char uc_j;
    int k;
    char *p_tmp = NULL;
    int i_tmp = 0;
    Frstr *p_frstr = NULL;

    if (ems_readUnsignedShort(p_st_ems_packet, &us_i) != FR_SUCC)
        goto error;

    if (us_i == 0)
        return FR_SUCC;

    if (ems_readUnsignedByte(p_st_ems_packet, &uc_j) != FR_SUCC)
        goto error;

    if ((p_frstr = frstr_new(us_i)) == NULL)
        goto error;

    for (k = 0; k < uc_j; k++)
    {
        if (ems_readString(p_st_sess, p_st_ems_packet, FR_YES, &p_tmp, &i_tmp) != FR_SUCC)
            goto error;
        if (FRSTR_LEN(p_frstr) > 0)
        {
            if (FRSTR_APPEND_STR(p_frstr, ".") != FR_SUCC)
                goto error;
        }
        if (FRSTR_APPEND_STR(p_frstr, p_tmp) != FR_SUCC)
            goto error;
        ems_free((void **) &p_tmp);
    }

    *p_i_out_len = FRSTR_LEN(p_frstr);
    *pp_out = frstr_free2(&p_frstr);

    return FR_SUCC;
error:
    ems_free((void **) &p_tmp);
    frstr_free(&p_frstr);
    return FR_FAIL;
}

int ems_readDestination(L7session_ems *p_st_sess
                        , L7_ems_packet *p_st_ems_packet
                        , char **pp_out
                        , int *p_i_out_len)
{
    unsigned char uc_i;

    if (ems_readUnsignedByte(p_st_ems_packet, &uc_i) != FR_SUCC)
        goto error;

    switch (uc_i)
    {
        case 0:
            *pp_out = NULL;
            *p_i_out_len = 0;
            break;
        case 1:
        case 2:
            if (ems_readRvSubject(p_st_sess, p_st_ems_packet, pp_out, p_i_out_len) != FR_SUCC)
                goto error;
            break;
        default:
            goto error;
    }

    return FR_SUCC;
error:
    return FR_FAIL;
}

int ems_skipBytes(L7_ems_packet *p_st_ems_packet)
{
    long long ll_tmp = 0LL;
    int i_tmp = 0;

    if (ems_readVarLong(p_st_ems_packet, &ll_tmp) != FR_SUCC)
        goto error;

    if (ll_tmp == -1L || ll_tmp == 0)
        return FR_SUCC;

    i_tmp = (int) ll_tmp;

    if (bytebuff_skip(p_st_ems_packet->p_st_packet, (size_t) i_tmp) != FR_SUCC)
        goto error;

    return FR_SUCC;
error:
    return FR_FAIL;
}

int ems_skipSubmsg(L7_ems_packet *p_st_ems_packet)
{
    return bytebuff_skip(p_st_ems_packet->p_st_packet, 4);
}

int ems_skipArray(L7_ems_packet *p_st_ems_packet
                , int i_array_type)
{
    long long ll_tmp = 0LL;
    int k;
    int j;
    int i;

    if (ems_readVarLong(p_st_ems_packet, &ll_tmp) != FR_SUCC)
        goto error;

    if (ll_tmp > 2147483647LL)
        goto error;

    k = (int) ll_tmp;
    switch (i_array_type)
    {
        case 12:    // _SHORT_ARRAY_
            j = k / 2;
            for (i = 0; i < j; i++)
            {
                if (ems_skipShort(p_st_ems_packet) != FR_SUCC)
                    goto error;
            }
            break;
        case 13:    // _INT_ARRAY_
            j = k / 4;
            for (i = 0; i < j; i++)
            {
                if (ems_skipInt(p_st_ems_packet) != FR_SUCC)
                    goto error;
            }
            break;
        case 14:    // _LONG_ARRAY_
            j = k / 8;
            for (i = 0; i < j; i++)
            {
                if (ems_skipLong(p_st_ems_packet) != FR_SUCC)
                    goto error;
            }
            break;
        case 15:    // _FLOAT_ARRAY_
            j = k / 4;
            for (i = 0; i < j; i++)
            {
                if (ems_skipFloat(p_st_ems_packet) != FR_SUCC)
                    goto error;
            }
            break;
        case 16:    // _DOUBLE_ARRAY_
            j = k / 8;
            for (i = 0; i < j; i++)
            {
                if (ems_skipDouble(p_st_ems_packet) != FR_SUCC)
                    goto error;
            }
            break;
        default:
            goto error;
    }

    return FR_SUCC;
error:
    return FR_FAIL;
}

int ems_readObject(L7session_ems *p_st_sess
                , L7_ems_packet *p_st_ems_packet
                , Ems_object *p_st_object)
{
    unsigned char uc_i;
    int i_tmp_len;

    if (ems_readUnsignedByte(p_st_ems_packet, &uc_i) != FR_SUCC)
        goto error;

    p_st_object->type = (Ems_object_type) uc_i;
    switch (p_st_object->type)
    {
        case EMS_OBJECT_TYPE_NULL: // 0
            break;
        case EMS_OBJECT_TYPE_BOOL: // 1
            if (ems_readBoolean(p_st_ems_packet, &(p_st_object->value.c_bool)) != FR_SUCC)
                goto error;
            break;
        case EMS_OBJECT_TYPE_BYTE: // 2
            if (ems_readByte(p_st_ems_packet, &(p_st_object->value.c_byte)) != FR_SUCC)
                goto error;
            break;
        case EMS_OBJECT_TYPE_WCHAR: // 3
            if (ems_readChar(p_st_ems_packet, &(p_st_object->value.s_wchar)) != FR_SUCC)
                goto error;
            break;
        case EMS_OBJECT_TYPE_SHORT: // 4
            if (ems_readShort(p_st_ems_packet, &(p_st_object->value.s_short)) != FR_SUCC)
                goto error;
            break;
        case EMS_OBJECT_TYPE_INT: // 5
            if (ems_readInt(p_st_ems_packet, &(p_st_object->value.i_int)) != FR_SUCC)
                goto error;
            break;
        case EMS_OBJECT_TYPE_LONG: // 6
            if (ems_readLong(p_st_ems_packet, &(p_st_object->value.ll_long)) != FR_SUCC)
                goto error;
            break;
        case EMS_OBJECT_TYPE_FLOAT: // 7
            if (ems_readFloat(p_st_ems_packet, &(p_st_object->value.f_float)) != FR_SUCC)
                goto error;
            break;
        case EMS_OBJECT_TYPE_DOUBLE: // 8
            if (ems_readDouble(p_st_ems_packet, &(p_st_object->value.d_double)) != FR_SUCC)
                goto error;
            break;
        case EMS_OBJECT_TYPE_UTF8: // 9
            if (ems_readString(p_st_sess, p_st_ems_packet, FR_NO, &(p_st_object->value.p_utf8), &i_tmp_len) != FR_SUCC)
                goto error;
            break;
        case EMS_OBJECT_TYPE_BYTES: // 10
            if (ems_skipBytes(p_st_ems_packet) != FR_SUCC)
                goto error;
            break;
        case EMS_OBJECT_TYPE_SUBMAP: // 11
        case EMS_OBJECT_TYPE_SUBSTREAM: // 18
            if (ems_skipSubmsg(p_st_ems_packet) != FR_SUCC)
                goto error;
            break;
        case EMS_OBJECT_TYPE_SHORTA_ARRAY: // 12
        case EMS_OBJECT_TYPE_INT_ARRAY: // 13
        case EMS_OBJECT_TYPE_LONG_ARRAY: // 14
        case EMS_OBJECT_TYPE_FLOAT_ARRAY: // 15
        case EMS_OBJECT_TYPE_DOUBLE_ARRAY: // 16
            if (ems_skipArray(p_st_ems_packet, uc_i) != FR_SUCC)
                goto error;
            break;
        case EMS_OBJECT_TYPE_VARLONG: // 17
            if (ems_readVarLong(p_st_ems_packet, &(p_st_object->value.ll_varlong)) != FR_SUCC)
                goto error;
            break;
        default:
            goto error;
    }

    return FR_SUCC;
error:
    return FR_FAIL;
}

int ems_skipObject(L7session_ems *p_st_sess
                   , L7_ems_packet *p_st_ems_packet)
{
    unsigned char uc_i;
    char *p_tmp = NULL;
    int i_tmp_len;

    if (ems_readUnsignedByte(p_st_ems_packet, &uc_i) != FR_SUCC)
        goto error;

//    EMS_PRINT_WARN("object->type:[%d][%s]\n", uc_i, EMS_OBJECT_TYPE2STR(uc_i));
    switch (uc_i)
    {
        case EMS_OBJECT_TYPE_NULL: // 0
            break;
        case EMS_OBJECT_TYPE_BOOL: // 1
        case EMS_OBJECT_TYPE_BYTE: // 2
            if (bytebuff_skip(p_st_ems_packet->p_st_packet, 1) != FR_SUCC)
                goto error;
            break;
        case EMS_OBJECT_TYPE_WCHAR: // 3
        case EMS_OBJECT_TYPE_SHORT: // 4
            if (bytebuff_skip(p_st_ems_packet->p_st_packet, 2) != FR_SUCC)
                goto error;
            break;
        case EMS_OBJECT_TYPE_INT: // 5
            if (bytebuff_skip(p_st_ems_packet->p_st_packet, 4) != FR_SUCC)
                goto error;
            break;
        case EMS_OBJECT_TYPE_LONG: // 6
            if (bytebuff_skip(p_st_ems_packet->p_st_packet, 8) != FR_SUCC)
                goto error;
            break;
        case EMS_OBJECT_TYPE_FLOAT: // 7
            if (bytebuff_skip(p_st_ems_packet->p_st_packet, 4) != FR_SUCC)
                goto error;
            break;
        case EMS_OBJECT_TYPE_DOUBLE: // 8
            if (bytebuff_skip(p_st_ems_packet->p_st_packet, 8) != FR_SUCC)
                goto error;
            break;
        case EMS_OBJECT_TYPE_UTF8: // 9
            if (ems_readString(p_st_sess, p_st_ems_packet, FR_NO, &p_tmp, &i_tmp_len) != FR_SUCC)
                goto error;
            EMS_PRINT_DBG("%s\n", p_tmp);
            break;
        case EMS_OBJECT_TYPE_BYTES: // 10
            if (ems_skipBytes(p_st_ems_packet) != FR_SUCC)
                goto error;
            break;
        case EMS_OBJECT_TYPE_SUBMAP: // 11
        case EMS_OBJECT_TYPE_SUBSTREAM: // 18
            if (ems_skipSubmsg(p_st_ems_packet) != FR_SUCC)
                goto error;
            break;
        case EMS_OBJECT_TYPE_SHORTA_ARRAY: // 12
        case EMS_OBJECT_TYPE_INT_ARRAY: // 13
        case EMS_OBJECT_TYPE_LONG_ARRAY: // 14
        case EMS_OBJECT_TYPE_FLOAT_ARRAY: // 15
        case EMS_OBJECT_TYPE_DOUBLE_ARRAY: // 16
            if (ems_skipArray(p_st_ems_packet, uc_i) != FR_SUCC)
                goto error;
            break;
        case EMS_OBJECT_TYPE_VARLONG: // 17
            if (ems_skipVarLong(p_st_ems_packet) != FR_SUCC)
                goto error;
            break;
        default:
            goto error;
    }

    return FR_SUCC;
error:
    return FR_FAIL;
}

int ems_readTypedLong(L7_ems_packet *p_st_ems_packet
                    , long long *p_ll_out)
{
    unsigned char uc_i;
    int i_tmp;

    if (ems_readUnsignedByte(p_st_ems_packet, &uc_i) != FR_SUCC)
        goto error;

    if (uc_i == 2)
        return ems_readUnsignedByte(p_st_ems_packet, (unsigned char *) &p_ll_out);

    if (uc_i == 4)
        return ems_readUnsignedShort(p_st_ems_packet, (unsigned short *) &p_ll_out);

    if (uc_i == 5)
    {
        if (ems_readInt(p_st_ems_packet, &i_tmp) != FR_SUCC)
            goto error;
        *p_ll_out = i_tmp & 0xFFFFFFFFFFFFFFFFLL;
        return FR_SUCC;
    }

    return ems_readLong(p_st_ems_packet, p_ll_out);

error:
    return FR_FAIL;
}

int ems_readCompressed(L7_ems_packet *p_st_ems_packet
                    , char **pp_out
                    , int *p_i_out_len)
{
    long long ll_l1;
    long long ll_l2;
    int i;
    char *p_tmp = NULL;
    char *p_inflated = NULL;
    unsigned long ul_inflated_len;

    if (ems_readVarLong(p_st_ems_packet, &ll_l1) != FR_SUCC)
        goto error;

    if (ll_l1 == -1LL)
    {
        *pp_out = NULL;
        *p_i_out_len = 0;
        return FR_SUCC;
    }

    if (ll_l1 == 0)
        return ems_readBytes(p_st_ems_packet, pp_out, p_i_out_len);

    if (ems_readVarLong(p_st_ems_packet, &ll_l2) != FR_SUCC)
        goto error;

    if (ll_l2 == -1LL || ll_l2 == 0)
    {
        *pp_out = NULL;
        *p_i_out_len = 0;
        return FR_SUCC;
    }

    i = (int) ll_l2;

    if ((p_tmp = (char *) frmalloc(sizeof(char) * i)) == NULL)
    {
        EMS_MALLOCK_ERR;
        goto error;
    }
    if (bytebuff_get_bytes(p_st_ems_packet->p_st_packet, p_tmp, (unsigned int) i) != FR_SUCC)
        goto error;

    if (frzlib_inflater((Byte *) p_tmp, (uLong) i, &p_inflated, &ul_inflated_len) != FR_SUCC)
    {
        EMS_PRINT_WARN("inflate failed\n");
        goto error;
    }
//    PRINT_HEX_STRING("inflate ok", p_inflated, (unsigned int) ul_inflated_len);

    *pp_out = p_inflated;
    *p_i_out_len = (int) ul_inflated_len;

    ems_free((void **) &p_tmp);
    return FR_SUCC;
error:
    ems_free((void **) &p_tmp);
    ems_free((void **) &p_inflated);
    return FR_FAIL;
}
